package com.test.spring.context;

import com.test.spring.annotation.Autowired;
import com.test.spring.annotation.Component;
import com.test.spring.annotation.ComponentScan;
import com.test.spring.annotation.Scope;
import com.test.spring.beans.BeanDefinition;
import com.test.spring.beans.BeanNameAware;
import com.test.spring.beans.BeanPostProcessor;
import com.test.spring.beans.InitializingBean;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContext {
    /**
     * 配置类
     */
    private Class configClass;
    /**
     * 单例池，里面存储的是单例对象
     */
    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<String,Object>();
    /**
     * 存放BeanDefinition对象
     */
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<BeanPostProcessor>();
    public ApplicationContext(Class configClass) {
        this.configClass = configClass;
        // 初始化Bean容器
        // Scan方法将BeanDefinition对象加入到BeanDefinitionMap中，
        scan(configClass);
        //BeandDefinition容器里的元素来一一将扫描路径下的对象的BeanDefinition对象取出
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            BeanDefinition beanDefinition = beanDefinitionEntry.getValue();
            // scope属性来判断对象是单例还是多例
            if ("singleton".equals(beanDefinition.getScope())){
                // 说明是单例Bean，将单例Bean加入到单例池中
                Object bean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,bean);
            }
        }
    }
    public Object createBean(String beanNames,BeanDefinition beanDefinition){
        ConcurrentHashMap<String,BeanDefinition> internalBeanDefinitionMap = new ConcurrentHashMap<String,BeanDefinition>();
        // 通过BeanDefinition的class属性来通过反射创建出对象
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            for (Field declaredField : clazz.getDeclaredFields()) {
                // 判断属性上是否包含了Autowired注解
                if (declaredField.isAnnotationPresent(Autowired.class)){
                    // 进入到这一步就说明已经包含了AutoWired注解
                    // 接下来获取包含@Autowired注解的属性的属性
                    Class<?> type = declaredField.getType();
                    // 从BeanDefinitionMap中遍历，找到和包含@Autowired注解的属性的属性相同的BeanDefinition，并把BeanDefinition存入到internalBeanDefinitionMap
                    for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
                        // 进行比较
                        if (entry.getValue().getClazz().equals(type)){
                            internalBeanDefinitionMap.put(entry.getKey(), entry.getValue());
                        }
                    }
                    // 判断internalBeanDefinitionMap里有几个元素，如果大于1，就说明相同类型有两个，我们就通过ByName来进行注入
                    if (internalBeanDefinitionMap.size()>1){
                        // 先获取有@AutoWired注解的属性的名称
                        String beanName = declaredField.getName();
                        // 不断从internalBeanDefinitionMap中进行比较
                        for (Map.Entry<String, BeanDefinition> entry : internalBeanDefinitionMap.entrySet()) {
                            if (beanName.equals(entry.getKey())){
                                // 到这一步就说明Name有相同的了，通过name来调用getBean方法
                                Object bean = getBean(beanName);
                                // 属性注入
                                declaredField.setAccessible(true);
                                declaredField.set(instance,bean);
                                // 将内部的internalBeanDefinitionMap置空
                                internalBeanDefinitionMap.clear();
                                break;
                            }
                        }
                    }else {
                        // 这一步就说明容器中类型相同的只有1个
                        Object bean = getBean(declaredField.getName());
                        declaredField.setAccessible(true);
                        declaredField.set(instance, bean);
                        // 将内部的internalBeanDefinitionMap置空
                        internalBeanDefinitionMap.clear();
                    }
                }
            }
            // Aware回调
            if (instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanNames);
            }
            // 初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanNames);
            }
            // Bean初始化
            if (instance instanceof InitializingBean){
                ((InitializingBean) instance).afterPropertiesSet();
            }
            // 初始化后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanNames);
            }
            return instance;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
    private void scan(Class configClass){
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        // 获取扫描路径
        String path = componentScanAnnotation.value();
        ClassLoader classLoader = ApplicationContext.class.getClassLoader();
        path = path.replace(".", "//");
        // 这个resource是一个目录
        URL resource = classLoader.getResource(path);
        // 通过File类来获取目录下的文件
        File file = new File(resource.getFile());
        // 如果文件是一个文件夹，则获取文件夹下的所有的文件
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String fileName = f.getAbsolutePath();
                // 判断文件是不是class文件，如果是的话在开始截取
                if (fileName.endsWith(".class")) {
                    // 从test开始截取，到.class结束
                    String className = fileName.substring(fileName.indexOf("test"), fileName.indexOf(".class"));
                    // 将\转换为.
                    className = className.replace("\\", ".");
                    // 得到了类的路径
                    String[] split = className.split("\\.", 2);
                    className = split[1];
                    try {
                        // 通过类加载器来加载类
                        Class<?> clazz = classLoader.loadClass(className);
                        // 判断类上是否有Component注解
                        if (clazz.isAnnotationPresent(Component.class)) {
                            // 判断类是否继承了BeanPostProcessor
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                // 如果继承了，就创建这个BeanPostProcessor对象，并添加到list中
                                try {
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                                    beanPostProcessorList.add(instance);
                                } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                                    e.printStackTrace();
                                }
                            }
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            // 生成BeanDefinitation对象
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            if (clazz.isAnnotationPresent(Scope.class)){
                                // 设置BeanDefinitation对象的scope属性
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefinition.setScope(value);
                            }else {
                                // Bean中没有scope注解，则将BeanDefinition的scope属性设置为singleton，即单例
                                beanDefinition.setScope("singleton");
                            }
                            // 将BeanDefinition对象加入到BeanDefinitionMap中
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    public Object getBean(String beanName) {
        if (beanDefinitionMap.containsKey(beanName)){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if ("singleton".equals(beanDefinition.getScope())){
                // 说明这是单例Bean，从单例池子中获取Bean对象返回
                return singletonObjects.get(beanName);
            }else {
                // 说明这是多例Bean 手动创建Bean对象并返回
                return createBean(beanName,beanDefinition);
            }
        }else {
            throw new RuntimeException("没有"+beanName+"对象");
        }

    }
}
